package com.axapoint.opoc.core.dao;

import java.util.Collection;
import java.util.LinkedList;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

import org.hibernate.annotations.Cascade;
import org.joda.time.DateTime;
import org.joda.time.Interval;

import com.anasoft.os.daofusion.bitemporal.WrappedBitemporalProperty;
import com.anasoft.os.daofusion.bitemporal.WrappedValueAccessor;

@Entity
@Table(name = "ExternalReference")
public class ExternalReference extends ObjectRegistration<ExternalReference, ExternalReference.Value, ExternalReference.Bitemporal> {
	private static final long serialVersionUID = 9168710745762860711L;

	@Entity
	@Table(name = "ExternalReferenceValues")
	public static final class Value extends _Value<ExternalReference, Bitemporal> {
		private static final long serialVersionUID = 1L;

		//@Column(unique=true)
		private String externalSystemId;
		private String tag;  // The external systems tag for entity
		
		// Entities:
		
		@SuppressWarnings("deprecation")
		@ManyToOne(optional=true, fetch = FetchType.LAZY)
	    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	    @JoinColumn(name="organizationId")
		private Organization organization;

		@SuppressWarnings("deprecation")
		@ManyToOne(optional=true, fetch = FetchType.LAZY)
	    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	    @JoinColumn(name="personId")
		private Person person;
				
		@SuppressWarnings("deprecation")
		@ManyToOne(optional=true, fetch = FetchType.LAZY)
	    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	    @JoinColumn(name="locationId")
		private Location location;

		@SuppressWarnings("deprecation")
		@ManyToOne(optional=true, fetch = FetchType.LAZY)
	    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
	    @JoinColumn(name="unitId")
		private Unit unit;

		protected Value() {};
		Value(ExternalReference er, String systemId, String userId, Operation operation, String comment, Value value) {
			super(er, systemId, userId, operation, comment);
			this.tag = value.tag;
			this.externalSystemId = value.externalSystemId;
			this.unit = value.unit;
			this.person = value.person;
			this.location = value.location;
			this.organization = value.organization;
		}
		public Value(String externalSystemId, String tag, Location location) {
			super();
			this.tag = tag;
			this.externalSystemId = externalSystemId;
			this.unit = null;
			this.person = null;
			this.location = location;
			this.organization = null;
		}
		public Value(String externalSystemId, String tag, Organization organization) {
			super();
			this.tag = tag;
			this.externalSystemId = externalSystemId;
			this.unit = null;
			this.person = null;
			this.location = null;
			this.organization = organization;
		}
		public Value(String externalSystemId, String tag, Person person) {
			super();
			this.tag = tag;
			this.externalSystemId = externalSystemId;
			this.unit = null;
			this.person = person;
			this.location = null;
			this.organization = null;
		}
		public Value(String externalSystemId, String tag, Unit unit) {
			super();
			this.tag = tag;
			this.externalSystemId = externalSystemId;
			this.unit = unit;
			this.person = null;
			this.location = null;
			this.organization = null;
		}
		@Override
		public String diff(_Value<ExternalReference, Bitemporal> otherValue, DateTime t0, DateTime t1) {
			StringBuffer sb = new StringBuffer();
			Value other = (Value)otherValue;
			

			Location.Bitemporal l0 = getLocation().getValues().get(t0);
			Location.Bitemporal l1 = getLocation().getValues().get(t1);

			if (!l0.getValue().getEntity().equals(l1.getValue().getEntity()) ||
				!l0.getValue().equals(l1.getValue())) {
				sb.append("UnitLocation.location: ");
				sb.append('[').append(l0.getValue().getEntity().getUuid()).append("] ").append(l0.getValue().getAddress()).append(" - ");
				sb.append('[').append(l0.getValue().getEntity().getUuid()).append("] ").append(l1.getValue().getAddress()).append('\n');
			}
			Unit.Bitemporal u0 = getUnit().getValues().get(t0);
			Unit.Bitemporal u1 = getUnit().getValues().get(t1);

			if (!u0.getValue().getEntity().equals(u1.getValue().getEntity()) ||
				!u0.getValue().equals(u1.getValue())) {
				sb.append("UnitLocation.unit: ");
				sb.append('[').append(u0.getValue().getEntity().getUuid()).append("] ").append(u0.getValue().getName()).append(" - ");
				sb.append('[').append(u0.getValue().getEntity().getUuid()).append("] ").append(u1.getValue().getName()).append('\n');
			}
			
			return sb.toString();
		}
		@Override
		public String toString() {
			StringBuffer sb = new StringBuffer();
			sb.append(super.toString());
			sb.append("  externalSystem=").append(externalSystemId).append('\n');
			sb.append("  tag=").append(tag).append('\n');
			if (unit != null)
				sb.append("  unit=").append(unit.getValues().get(getBitemporal().getValidityInterval().getStart()).getValue().getName()).append('\n');
			if (person != null)
				sb.append("  person=").append(person.getValues().get(getBitemporal().getValidityInterval().getStart()).getValue().getCprNo()).append('\n');
			if (location != null)
				sb.append("  location=").append(location.getValues().get(getBitemporal().getValidityInterval().getStart()).getValue().getAddress()).append('\n');
			if (organization != null)
				sb.append("  organization=").append(organization.getValues().get(getBitemporal().getValidityInterval().getStart()).getValue().getName()).append('\n');
			
			return sb.toString();
		}
		public String getExternalSystemId() {
			return externalSystemId;
		}
		protected void setExternalSystemId(String externalSystemId) {
			this.externalSystemId = externalSystemId;
		}
		public String getTag() {
			return tag;
		}
		protected void setTag(String tag) {
			this.tag = tag;
		}
		public Organization getOrganization() {
			return organization;
		}
		protected void setOrganization(Organization organization) {
			this.organization = organization;
		}
		public Person getPerson() {
			return person;
		}
		protected void setPerson(Person person) {
			this.person = person;
		}
		public Location getLocation() {
			return location;
		}
		protected void setLocation(Location location) {
			this.location = location;
		}
		public Unit getUnit() {
			return unit;
		}
		protected void setUnit(Unit unit) {
			this.unit = unit;
		}
	}
	
	@Entity
	@Table(name = "ExternalReferenceBitemporal")
	public static final class Bitemporal extends _Bitemporal<Bitemporal, Value> {
		private static final long serialVersionUID = -2132187534862370208L;
		
		protected Bitemporal() {}
	    private Bitemporal(ExternalReference.Value value, Interval validityInterval) {
	        super(value, validityInterval);
	    }
	    
	    @Override
	    public Bitemporal copyWith(Interval validityInterval) {
	    	return new Bitemporal(value, validityInterval);
	    }
	}

	@SuppressWarnings("deprecation")
	@OneToMany(fetch = FetchType.LAZY)
    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
    @JoinColumn(name="organizationChannelId")
    private Collection<OrganizationChannel.Value> organizationChannels = new LinkedList<OrganizationChannel.Value>();	

	@SuppressWarnings("deprecation")
	@OneToMany(fetch = FetchType.LAZY)
    @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
    @JoinColumn(name="engagementId")
    private Collection<Engagement.Value> engagements = new LinkedList<Engagement.Value>();	

	public ExternalReference() {
		super();
	}
    public Collection<OrganizationChannel.Value> getOrganizationChannels() {
		return organizationChannels;
	}
	protected void setOrganizationChannels(
			Collection<OrganizationChannel.Value> organizationChannels) {
		this.organizationChannels = organizationChannels;
	}
	public Collection<Engagement.Value> getEngagements() {
		return engagements;
	}
	protected void setEngagements(Collection<Engagement.Value> engagements) {
		this.engagements = engagements;
	}
	public WrappedBitemporalProperty<Value, Bitemporal> getValues() {
    	return new WrappedBitemporalProperty<Value, Bitemporal>(values, new WrappedValueAccessor<Value,Bitemporal>() {
			private static final long serialVersionUID = 1L;

			@Override
			public Bitemporal wrapValue(Value value, Interval validityInterval) {
				Bitemporal bi = new Bitemporal(value, validityInterval);
				return bi;
			}
    	});
    }
}
